#include "RoutingBoundary.h"
#include "Grids.h"
#include "Route.h"
#include "contest_dm.h"
#include "idm.h"
#include "contest_coord.h"
#include "./Third/CoordEncoding/CoordinateCodec.hpp"

namespace ieda_contest {

RoutingBoundaryInfo::RoutingBoundaryInfo(ContestCoord first_coordinate,
                                        ContestCoord second_coordinate,
                                        cell_move_router::CoordinateCodec<unsigned long long> &&Codec,
                                        std::unordered_set<size_t> &&Terminals,
                                        std::vector<ContestSegment> &&RouteUnderMinLayer):
                                        _first_coordinate(first_coordinate), _second_coordinate(second_coordinate), _Codec(std::move(Codec)),
                                        _Terminals(std::move(Terminals)), _RouteUnderMinLayer(std::move(RouteUnderMinLayer)) {}

BoundaryBuilder::BoundaryBuilder(ContestDataManager* data_manager,
                    ContestNet *SingeNetPtr,
                    std::vector<ContestSegment> &OriginRoute):
                    _data_manager(data_manager), _SingeNetPtr(SingeNetPtr), _OriginRoute(OriginRoute),
                    _Codec({0, 0, 0}) {

                        //_data_manager = data_manager;
                        //_SingeNetPtr = SingeNetPtr;
                        //_OriginRoute = OriginRoute;
                
                    }

bool BoundaryBuilder::checkPins() const {
    ContestNet* SingNetPtr = getSingleNetPtr();
    Route Route_tmp(_data_manager);
    // 遍历每一条Net上的pin，一旦出现overflow，就输出false
    for (ContestPin &Pin : SingNetPtr->get_pin_list()) {
        int x_coord = Pin.get_grid_coord().get_x();
        int y_coord = Pin.get_grid_coord().get_y();
        int LayerID = Pin.get_grid_coord().get_layer_idx();
        if (Route_tmp.FindCellInMap(x_coord, y_coord, LayerID).get_cell_supply() <= 0) {
            return false;
        }
    }
    return true;
}

bool BoundaryBuilder::checkRoute(ContestSegment &RouteSegement) const {
    Route Route_tmp(_data_manager);
    int x1 = RouteSegement.get_first().get_x();
    int y1 = RouteSegement.get_second().get_y();
    // 遍历一个routing segment
    // case1: 不跨层，就考虑第一个起始点有没有overflow
    // case2: 如果跨层，就考虑每一个点有没有overflow
    // 一旦overflow 就输出false
    for (int LayerID = RouteSegement.get_first().get_layer_idx(); LayerID <= RouteSegement.get_second().get_layer_idx(); LayerID++) {
        if (Route_tmp.FindCellInMap(x1, y1, LayerID).get_cell_supply() <= 0) {
            return false;
        }
    }
    return true;
}

void BoundaryBuilder::set_CodeC(int x, int y, int L) {
    _Codec = cell_move_router::CoordinateCodec<unsigned long long> (
        {
            (unsigned long long)(x),
            (unsigned long long)(y),
            (unsigned long long)(L),
        }
    );
}

void BoundaryBuilder::createBoundaryInfo() {
    // 1. 先检查pin是否通过检查，如果没有通过，checkPins会输出false，
    // 因此条件变成true，成立。 设置BoundaryInfoExist为false，中止程序
    if (!checkPins()) {
        setBoundaryInfoExist(false);
        return;
    }

    // 2. 获取整个design的start position和end position, index从0开始
    int start_x = _data_manager->get_database()->get_grid_list()[0].contest_get_start();
    int start_y = _data_manager->get_database()->get_grid_list()[1].contest_get_start();
    int num_x = _data_manager->get_database()->get_grid_list()[0].contest_get_num();
    int num_y = _data_manager->get_database()->get_grid_list()[1].contest_get_num();
    int end_x = start_x + num_x - 1;
    int end_y = start_y + num_y -1;
    int total_routing_layer = _data_manager->get_idb_layout()->get_layers()->get_routing_layers_number();
    int start_layer_id = 0;
    int end_layer_id = start_layer_id + total_routing_layer - 1;

    // 3. 遍历网络的所有引脚和原始路由，更新最小和最大行、列和层变量，以确定路由边界
    int Min_X = end_x;
    int Min_Y = end_y;
    int Max_X = start_x;
    int Max_Y = start_y;
    int Min_L = end_layer_id;
    int Max_L = start_layer_id;
    ContestNet* SingNetPtr = getSingleNetPtr();
    Route Route_tmp(_data_manager);
    // 3.1 for pin
    for (ContestPin &Pin : SingNetPtr->get_pin_list()) {
        int x_coord = Pin.get_grid_coord().get_x();
        int y_coord = Pin.get_grid_coord().get_y();
        int LayerID = Pin.get_grid_coord().get_layer_idx();
        Min_X = std::min(Min_X, x_coord);
        Max_X = std::max(Max_X, x_coord);
        Min_Y = std::min(Min_Y, y_coord);
        Max_Y = std::max(Max_Y, y_coord);
        Min_L = std::min(Min_L, LayerID);
        Max_L = std::max(Max_L, LayerID);
    }
    // 3.2 for routes
    for (ContestSegment &routingSegement : _OriginRoute) {
        Min_X = std::min(Min_X, routingSegement.get_first().get_x());
        Max_X = std::max(Max_X, routingSegement.get_second().get_x());
        Min_Y = std::min(Min_Y, routingSegement.get_first().get_y());
        Max_Y = std::max(Max_Y, routingSegement.get_second().get_y());
        Min_L = std::min(Min_L, routingSegement.get_first().get_layer_idx());
        Max_L = std::max(Max_L, routingSegement.get_second().get_layer_idx());
    }
    // 3.3 enlarge rerouting area, trade-off between performance and time
    int expand_xy = 6;  // hyperparameter 1
    int expand_L = 2;   // hyperparameter 2
    Min_X = std::max(Min_X - expand_xy, start_x);
    Max_X = std::min(Max_X + expand_xy, end_x);
    Min_Y = std::max(Min_Y - expand_xy, start_y);
    Max_Y = std::min(Max_Y + expand_xy, end_y);
    Min_L = std::max(Min_L - expand_L, start_layer_id);
    Max_L = std::min(Max_L + expand_L, end_layer_id);

    // 使用CoordinateCodec来编码边界区域内的坐标，用于后续处理。(我们用contestCoord类就行)
    ContestCoord start_coordinate(Min_X, Min_Y, Min_L);
    ContestCoord end_coordinate(Max_X, Max_Y, Max_L);

    setStartCoordinate(start_coordinate);
    setEndCoordinate(end_coordinate);

    set_CodeC(Max_X - Min_X + 1, Max_Y - Min_Y + 1, Max_L - Min_L + 1);
    // 处理网络引脚，生成终端（Terminals）集合，这些终端用于Steiner树等算法的输入。
    std::unordered_map<ContestCoord, int> PinMinLayer;
    clear_terminal();
    for (ContestPin &Pin : SingNetPtr->get_pin_list()) {
        int x_coord = Pin.get_grid_coord().get_x();
        int y_coord = Pin.get_grid_coord().get_y();
        int LayerID = Pin.get_grid_coord().get_layer_idx();
        size_t intoTerminal = get_CodeC().encode(
            {(unsigned long long)x_coord - Min_X, 
             (unsigned long long)y_coord - Min_Y, 
             (unsigned long long)std::max(LayerID, Min_L) - Min_L});
        set_termial(intoTerminal); 
        // 下面这个步骤看起来我们不会遇到，因为我们没有min layer的限制
        if (LayerID < Min_L) {
            ContestCoord tmpCoord(x_coord - Min_X, y_coord - Min_Y, 0);
            auto It = PinMinLayer.find(tmpCoord);
            if (It == PinMinLayer.end()) {
                PinMinLayer.emplace(tmpCoord, LayerID);
            } else {
                It->second = std::min(It->second, LayerID);
            }
        }
        // 为低于最小层的路由生成路由信息，检查这些路由的有效性
        // 最后，根据以上步骤的结果，设置BoundaryInfoExist标志，指示是否成功生成了路由边界信息。
        clear_routeUnderMinLayer();
        for (auto &P : PinMinLayer) {
            ContestCoord tmpCoord = P.first;
            int x_coord = tmpCoord.get_x() + Min_X;
            int y_coord = tmpCoord.get_y() + Min_Y;
            int Layer = P.second;
            const ContestCoord p1(x_coord, y_coord, Layer);
            const ContestCoord p2(x_coord, y_coord, Min_L);
            set_routeUnderMinLayer(p1, p2);
            if (!checkRoute(get_routeUnderMinLayer().back())) {
                setBoundaryInfoExist(false);
                return;
            }
        }
        setBoundaryInfoExist(true);
    }
}

RoutingBoundaryInfo BoundaryBuilder::getBoundaryInfo() {
    assert(isBoundaryInfoExist());
    setBoundaryInfoExist(false);
    return RoutingBoundaryInfo(getStartCoordinate(), getEndCoordinate(), std::move(get_CodeC()),
                                std::move(get_termial()), std::move(get_routeUnderMinLayer()));
}

} // ieda_contest